﻿using Castle.DynamicProxy;
using In66.Net.Caching.Interceptor.Castle;
using In66.Net.Repository;
using In66.Net.Repository.EFCoreData.MySql;
using In66.Net.Repository.Entities;
using In66.Net.Repository.IRepositories.ISqlSugar;
using In66.Net.Repository.SqlSugarData;
using In66.Net.Share.Application.Interceptor.EfUniOfWork;
using In66.Net.Share.Application.Interceptor.OperateLog;
using In66.Net.Share.Application.Interceptor.SqlSugarUnitOfWork;
using In66.Net.Share.Models.OrmType;
using Microsoft.EntityFrameworkCore;
using SqlSugar;
using StackExchange.Redis;

namespace In66.Net.Share.Registrar
{
    public abstract partial class AbstractWebApiDependencyRegistrar
    {
        #region 注册SqlSugarRepository

        /// <summary>
        /// 注册SqlSugarRepository服务
        /// </summary>
        protected virtual void AddContextSugarWithRepositories(Action<IServiceCollection>? action = null)
        {
            //UnitOfWork
            Services.AddSingleton<ISqlSugarUnitOfWork, SqlSugarUnitOfWork>();

            Services.AddScoped(typeof(ISugarBaseRepository<>), typeof(SqlSugarBaseRepository<>));

            Services.AddScoped<SqlSugarUowInterceptor>();

            Services.AddScoped<SqlSugarUowAsyncInterceptor>();

            AddSqlSugar();

            AddSerivcesWithInterceptors(OrmType.SqlSugar);
        }

        /// <summary>
        /// AddSqlSugar
        /// </summary>
        public virtual void AddSqlSugar()
        {
            var mysqlConfig = MysqlSection.Get<MysqlConfig>();

            //获取数据类型
            var dbType = mysqlConfig.DbType == DbType.MySql.ToString() ? DbType.MySql : DbType.SqlServer;

            ////判断是否开启redis设置二级缓存方式
            //ICacheService myCache = AppSettingsConstVars.RedisConfigEnabled
            //    ? (ICacheService)new SqlSugarRedisCache()
            //    : new SqlSugarMemoryCache();

            var connectionConfig = new ConnectionConfig()
            {
                ConnectionString = mysqlConfig.ConnectionString, //必填
                DbType = dbType, //必填
                IsAutoCloseConnection = false,
                InitKeyType = InitKeyType.Attribute,
            };

            Services.AddScoped<ISqlSugarClient>(o =>
            {
                var db = new SqlSugarScope(connectionConfig); //默认SystemTable

                #region Db

                //日志处理
                //SQL执行前 可以修改SQL
                db.Aop.OnLogExecuting = (sql, pars) =>
                {
                    //获取sql
                    Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
                    Console.WriteLine();

                    //通过TempItems这个变量来算出这个SQL执行时间（1）
                    if (db.TempItems == null)
                    {
                        db.TempItems = new Dictionary<string, object>();
                    }

                    db.TempItems.Add("logTime", DateTime.Now);
                    //通过TempItems这个变量来算出这个SQL执行时间（2）
                    var startingTime = db.TempItems["logTime"];
                    db.TempItems.Remove("time");
                    var completedTime = DateTime.Now;
                };
                db.Aop.OnLogExecuted = (sql, pars) => //SQL执行完事件
                {
                };
                db.Aop.OnLogExecuting = (sql, pars) => //SQL执行前事件
                {
                };
                db.Aop.OnError = (exp) =>//执行SQL 错误事件
                {
                    //NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Other, "SqlSugar", "执行SQL错误事件", exp);
                };

                #endregion

                return db;
            });

        }

        #endregion

        #region 注册EFRepository

        /// <summary>
        /// 注册Context与仓储
        /// </summary>
        protected virtual void AddContextWithRepositories()
        {
            var serviceType = typeof(IEntityInfo);
            var implType = ServiceInfo.StartAssembly.ExportedTypes.FirstOrDefault(type => type.IsAssignableTo(serviceType) && type.IsNotAbstractClass(true));
            if (implType is null)
            {
                throw new NotImplementedException(nameof(IEntityInfo));
            }
            else
            {
                Services.AddScoped(serviceType, implType);
            }

            AddContext();

            AddSerivcesWithInterceptors(OrmType.EF);

            Services.AddScoped<UowInterceptor>();

            Services.AddScoped<UowAsyncInterceptor>();
        }

        /// <summary>
        /// 注册Context
        /// </summary>
        protected virtual void AddContext()
        {
            var serviceInfo = Services.GetServiceInfo();
            var mysqlConfig = MysqlSection.Get<MysqlConfig>();
            var serverVersion = new MariaDbServerVersion(new Version(10, 5, 4));
            Services.AddInfraEfCoreMySql(options =>
            {
                //options.UseLowerCaseNamingConvention();
                options.UseSnakeCaseNamingConvention();
                options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
                {
                    optionsBuilder.MinBatchSize(4)
                                            .MigrationsAssembly(serviceInfo.Name)
                                            .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
                });
            });
        }

        #endregion

        /// <summary>
        /// 注册Serivces
        /// </summary>
        protected virtual void AddSerivcesWithInterceptors(OrmType ormType, Action<IServiceCollection>? action = null)
        {
            List<Type> DefaultInterceptorTypes = new List<Type>();

            if (ormType == OrmType.SqlSugar)
            {
                DefaultInterceptorTypes = new() { typeof(OperateLogInterceptor), typeof(CachingInterceptor), typeof(SqlSugarUowInterceptor) };
            }
            else
            {
                DefaultInterceptorTypes = new() { typeof(OperateLogInterceptor), typeof(CachingInterceptor), typeof(UowInterceptor) };
            }

            action?.Invoke(Services);

            var appServiceType = typeof(IAppService);
            var serviceTypes = ServiceInfo.StartAssembly.GetExportedTypes().Where(type => type.IsInterface && type.IsAssignableTo(appServiceType)).ToList();
            serviceTypes.ForEach(serviceType =>
            {
                var implType = ServiceInfo.StartAssembly.ExportedTypes.FirstOrDefault(type => type.IsAssignableTo(serviceType) && type.IsNotAbstractClass(true));
                if (implType is null)
                {
                    return;
                }

                Services.AddScoped(implType);
                Services.TryAddSingleton(new ProxyGenerator());
                Services.AddScoped(serviceType, provider =>
                {
                    var interfaceToProxy = serviceType;
                    var target = provider.GetService(implType);
                    var interceptors = DefaultInterceptorTypes.ConvertAll(interceptorType => provider.GetService(interceptorType) as IInterceptor).ToArray();
                    var proxyGenerator = provider.GetRequiredService<ProxyGenerator>();
                    var proxy = proxyGenerator.CreateInterfaceProxyWithTargetInterface(interfaceToProxy, target, interceptors);
                    return proxy;
                });
            });
        }

        /// <summary>
        /// 注册Application的IHostedService服务
        /// </summary>
        protected virtual void AddHostedServices()
        {
            var serviceType = typeof(IHostedService);
            var implTypes = Assembly.GetExecutingAssembly().ExportedTypes.Where(type => type.IsAssignableTo(serviceType) && type.IsNotAbstractClass(true)).ToList();
            implTypes.ForEach(implType =>
            {
                Services.AddSingleton(serviceType, implType);
            });
        }

        /// <summary>
        /// 注册Domain服务
        /// </summary>
        protected virtual void AddDomainSerivces<TDomainService>(Action<IServiceCollection>? action = null)
            where TDomainService : class
        {
            action?.Invoke(Services);

            var serviceType = typeof(TDomainService);
            var implTypes = ServiceInfo.StartAssembly.ExportedTypes.Where(type => type.IsAssignableTo(serviceType) && type.IsNotAbstractClass(true)).ToList();
            implTypes.ForEach(implType =>
            {
                Services.AddScoped(implType, implType);
            });
        }
    }
}
